package scales.xml.jaxen
import org.jaxen._
import pattern.Pattern
import expr._
import java.util.{Iterator => JIterator}
import scales.utils._
import ScalesUtils._
import scales.xml._
import ScalesXml._
import scales.xml.impl.{DocumentRoot, DocsUp}
import collection.DuplicateFilter
import scala.collection.JavaConversions._
object Implicits {
import scalaz.Equal._
import scalaz._
import Scalaz._
import xpath.PositionalEquals.{xpathPositionalEqual => xpathEqual}
implicit val eitherAOrXEqual =
equal[Either[AttributePath, XmlPath]] {
(a , b) =>
if (a.isLeft && b.isLeft) {
val al = a.left.get
val bl = b.left.get
if (al eq bl)
true
else
if (al.attribute == bl.attribute)
xpathEqual.equal(al.parent, bl.parent)
else
false
} else
if (a.isRight && b.isRight)
xpathEqual.equal(a.right.get, b.right.get)
else
false
}
}
object ScalesXPath {
val defaultNoConversion : QName => QName = identity
val localOnly : QName => QName = q => NoNamespaceQName(q.local)
def apply(xpath : String, nsMap : Map[String, String] = Map()) =
new ScalesXPath( xpath, nsMap)
def apply(xpath : String, nsMap : Iterable[ PrefixedNamespace ] ) =
new ScalesXPath(xpath, nsMap.map( p => p.prefix -> p.ns.uri ).toMap)
def apply(xpath : String, pre : PrefixedNamespace, pres : PrefixedNamespace * ) =
new ScalesXPath(xpath, (pre +: pres).map( p => p.prefix -> p.ns.uri ).toMap, defaultNoConversion)
}
class ScalesXPath(val xpath : String, val nsMap : Map[String,String] = Map(), val nameConversion : QName => QName = ScalesXPath.defaultNoConversion) extends ScalesBaseJaxenXPath(xpath, ScalesXPathFactory, new ScalesNavigator(nameConversion)) {
import Implicits._
{
val ns = new SimpleNamespaceContext()
nsMap.foreach{ (t) =>
ns.addNamespace(t._1,t._2)
}
setNamespaceContext(ns)
}
def withNameConversion( conversion : QName => QName ) =
new ScalesXPath( xpath, nsMap, conversion)
def evaluate( path : XmlPath ) : Iterable[Either[AttributePath, XmlPath]] = {
val res = selectNodes(path).asInstanceOf[java.util.List[AnyRef]]
if (res.size < 2)
if (res.size == 0)
Nil
else {
val it = res.get(0)
if (it eq null)
Nil
else
one(it match {
case x @ DocsUp(a : AttributePath, p) => Left(a)
case x @ DocsUp(xp : XmlPath, p) => Right(xp)
case DocumentRoot(r) => Right(r)
case x : XmlPath => Right(x)
})
}
else
DuplicateFilter(sortT[XmlItem, Elem, XCC, Either[AttributePath,XmlPath]](res.map{
case x @ DocsUp(a : AttributePath, p) => (Left(a), a.parent)
case x @ DocsUp(xp : XmlPath, p) => (Right(xp), xp)
case DocumentRoot(r) => (Right(r), r)
case x : XmlPath => (Right(x), x)
}).map{x => x._1})(eitherAOrXEqual)
}
def xmlPaths( path : XmlPath ) : Iterable[XmlPath] =
evaluate(path).collect{ case Right(x) => x}
def attributePaths( path : XmlPath ) : Iterable[AttributePath] =
evaluate(path).collect{ case Left(x) => x}
def get[T]( path : XmlPath ) : T = {
val res = evaluate(path : Object)
res.asInstanceOf[T]
}
}
object ScalesComparator extends java.util.Comparator[AnyRef] {
val nav = new ScalesNavigator(ScalesXPath.defaultNoConversion)
import nav._
def compare( o1 : AnyRef, o2 : AnyRef) = {
if (isNonChild(o1) && isNonChild(o2)) {
val p1 = getParentNode(o1)
val p2 = getParentNode(o2)
if (p1 == p2) {
if (isNamespace(o1) && isAttribute(o2))
-1
else
if (isNamespace(o2) && isAttribute(o1))
1
else compare(p1,p2)
} else compare(p1,p2)
} else {
def getPath(o : AnyRef) =
if (isAttribute(o))
(o : AttributePath).parent
else
if (isDocument(o))
o.asInstanceOf[DocumentRoot].xmlPath
else
o : XmlPath
val path1 = getPath(o1)
val path2 = getPath(o2)
0 - comparePathPositions(path1.position, path2.position)
}
}
def isNonChild( o : AnyRef) =
isAttribute(o) || isNamespace(o)
}
object ScalesXPathFactory extends DefaultXPathFactory {
override def createUnionExpr( lhs : Expr, rhs : Expr ) =
new ScalesUnionExpr( lhs, rhs )
override def createRelativeLocationPath() =
new ScalesDefaultLocationPath()
override def createAbsoluteLocationPath() =
new ScalesDefaultAbsoluteLocationPath()
}
class ScalesNavigator(val nameConversion : QName => QName) extends DefaultNavigator {
implicit def fromDocsUpX( ctx : AnyRef ) : XmlPath =
ctx.asInstanceOf[DocsUp[XmlPath]].what
implicit def fromDocsUpA( ctx : AnyRef ) : AttributePath =
ctx.asInstanceOf[DocsUp[AttributePath]].what
def use[T,W]( ctx : AnyRef, f : DocsUp[W] => T ) =
f( ctx.asInstanceOf[DocsUp[W]] )
def wrap[W]( ctx : AnyRef, f : DocsUp[W] => W ) =
use( ctx, (d : DocsUp[W]) => d.copy( what = f(d)))
override def getChildAxisIterator( ctx : AnyRef ) =
ctx match {
case dr @ DocumentRoot(r) => one(DocsUp(r,dr)).iterator
case DocsUp(x : XmlPath,d) => x.map(DocsUp(_,d)).iterator
case x : XmlPath =>
val root = DocumentRoot(rootPath(x))
x.map(DocsUp(_, root)).iterator
case _ => error("couldn't get childaxis "+ctx)
}
override def getParentAxisIterator( ctx : AnyRef ) =
one(getParentNode(ctx)).iterator
override def getAttributeAxisIterator( ctx : AnyRef ) = {
def attribs(d : DocsUp[XmlPath]) =
d.what.tree.section.
attributes.map(a => DocsUp(
xpath.AttributePath(a, d.what),
d.docroot)).iterator
ctx match {
case d @ DocsUp(x : XmlPath, r) if (!x.isItem) =>
attribs(d.asInstanceOf[DocsUp[XmlPath]])
case x : XmlPath if (!x.isItem) =>
val root = DocumentRoot(rootPath(x))
attribs(DocsUp(x, root))
case _ => Nil.iterator
}
}
override def getParentNode( ctx : AnyRef ) =
ctx match {
case DocsUp(xpath.AttributePath(a, x), d) => DocsUp(x,d)
case DocsUp(x : XmlPath, d) =>
if (x eq d.xmlPath)
d
else
DocsUp(x.zipUp,d)
case DocumentRoot(x) => null
case x : XmlPath =>
val root = DocumentRoot(rootPath(x))
if (x eq root.xmlPath)
root
else
DocsUp(x.zipUp, root)
case x => error("got x instead " + x)
}
def parseXPath( xpath : String ) = new ScalesXPath(xpath)
def getNamespacePrefix( ctx : AnyRef ) = error("no namespace nodes yet")
def getNamespaceStringValue( ctx : AnyRef ) = error("no namespace nodes yet")
def isNamespace( ctx : AnyRef ) = false
def getTextStringValue( ctx : AnyRef ) = text(ctx: XmlPath)
def getAttributeStringValue( ctx : AnyRef ) = ctx match {
case DocsUp(xpath.AttributePath(a, x),d) => a.value
case _ => error("not an attribute")
}
def isAttribute( ctx : AnyRef ) =
if (ctx.isInstanceOf[DocsUp[_]]) {
val docsUpWhat = ctx.asInstanceOf[DocsUp[_]].what
if (docsUpWhat.isInstanceOf[AttributePath])
true
else false
} else false
def getAttributeQName( ctx : AnyRef ) = ctx match {
case DocsUp(xpath.AttributePath(a, x),d) =>
if (nameConversion eq ScalesXPath.defaultNoConversion)
a.name.qName
else
nameConversion(a.name).qName
case _ => error("not an attribute")
}
def getAttributeName( ctx : AnyRef ) = ctx match {
case DocsUp(xpath.AttributePath(a, x),d) =>
if (nameConversion eq ScalesXPath.defaultNoConversion)
a.name.local
else
nameConversion(a.name).local
case _ => error("not an attribute")
}
def getAttributeNamespaceUri( ctx : AnyRef ) = ctx match {
case DocsUp(xpath.AttributePath(a, x),d) =>
if (nameConversion eq ScalesXPath.defaultNoConversion)
a.name.namespace.uri
else
nameConversion(a.name).namespace.uri
case _ => error("not an attribute")
}
def ( : AnyRef ) = text(ctx: XmlPath)
def pOr[T]( ctx: AnyRef, f : XmlPath => T, e : => T) =
if (ctx.isInstanceOf[DocsUp[_]]) {
val docsUpWhat = ctx.asInstanceOf[DocsUp[_]].what
if (docsUpWhat.isInstanceOf[XmlPath])
f(docsUpWhat.asInstanceOf[XmlPath])
else e
} else e
def pOrFalse(ctx : AnyRef, f : XmlPath => Boolean) =
pOr(ctx, f, false)
def iOrFalse(ctx : AnyRef, f : XmlPath => Boolean) =
pOr(ctx, (x) => if (x.isItem) f(x) else false, false)
def isProcessingInstruction( ctx : AnyRef ) = iOrFalse(ctx, _.item.isInstanceOf[PI])
def isText( ctx : AnyRef ) = iOrFalse(ctx, x =>
x.item.isInstanceOf[Text] ||
x.item.isInstanceOf[CData] )
def ( : AnyRef ) = iOrFalse(ctx, _.item.isInstanceOf[Comment])
def isDocument( ctx : AnyRef ) = ctx.isInstanceOf[DocumentRoot]
def isElement( ctx : AnyRef ) = pOrFalse(ctx,!_.isItem)
def getElementStringValue( ctx : AnyRef ) =
if (isElement(ctx))
text(ctx: XmlPath)
else null
def getElementQName( ctx : AnyRef ) =
if (nameConversion eq ScalesXPath.defaultNoConversion)
ctx.tree.section.name.qName
else
nameConversion(ctx.tree.section.name).qName
def getElementName( ctx : AnyRef ) =
if (nameConversion eq ScalesXPath.defaultNoConversion)
ctx.tree.section.name.local
else
nameConversion(ctx.tree.section.name).local
def getElementNamespaceUri( ctx : AnyRef ) =
if (nameConversion eq ScalesXPath.defaultNoConversion)
ctx.tree.section.name.namespace.uri
else
nameConversion(ctx.tree.section.name).namespace.uri
override def getDocument( uri : String ) = error("don't do doc lookups at all man")
override def getDocumentNode( ctx : AnyRef ) =
DocumentRoot(rootPath(ctx.asInstanceOf[XmlPath]))
}